﻿using System;
using System.Collections.Generic;
using System.IO;


namespace advadev.DataStorage
{

    /// <summary>
    /// Represents a generic entity xml directory data storage
    /// </summary>
    public class GenericEntityXmlDirectoryDataStorage : IGenericEntityDataStorage
    {

        /// <summary>
        /// Initializes a new instance of the
        /// GenericEntityXmlDirectoryDataStorage class by the given parameter
        /// </summary>
        /// <param name="pathName">
        /// A path name string that references the directory
        /// </param>
        /// <exception cref="ArgumentException">
        /// Will be thrown if the path name is not valid
        /// </exception>
        public GenericEntityXmlDirectoryDataStorage(string pathName)
        {
            if (string.IsNullOrEmpty(pathName))
            {
                throw new ArgumentException("invalid path name", "pathName");
            }
            PathName = pathName;
            if (PathName.EndsWith("\\"))
            {
                PathName = PathName.Substring(0, PathName.Length - 1);
            }
        }


        /// <summary>
        /// Gets or sets the path name
        /// </summary>
        private string PathName
        {
            get;
            set;
        }


        #region IGenericEntityDataStorage implementation
        /// <summary>
        /// Saves the given generic entity
        /// </summary>
        /// <param name="genericEntity">
        /// Generic entity to save
        /// </param>
        public void Save(GenericEntity genericEntity)
        {
            if (genericEntity == null)
            {
                return;
            }
            GenericEntityXmlFileDataStorage genericEntityXmlFileDataStorage = getGenericEntityXmlFileDataStorage(genericEntity.ApplicationKey, genericEntity.FullClassName);
            genericEntityXmlFileDataStorage.Save(genericEntity);
        }

        /// <summary>
        /// Saves the given list of generic entities
        /// </summary>
        /// <param name="genericEntities">
        /// List of generic entities to save
        /// </param>
        public void Save(IList<GenericEntity> genericEntities)
        {
            if (genericEntities == null || genericEntities.Count == 0)
            {
                return;
            }
            foreach (GenericEntity genericEntity in genericEntities)
            {
                GenericEntityXmlFileDataStorage genericEntityXmlFileDataStorage = getGenericEntityXmlFileDataStorage(genericEntity.ApplicationKey, genericEntity.FullClassName);
                genericEntityXmlFileDataStorage.Save(genericEntity);
            }
        }

        /// <summary>
        /// Finds the generic entity with the given id, application key and type
        /// </summary>
        /// <param name="id">
        /// Id of the generic entity
        /// </param>
        /// <param name="applicationKey">
        /// Application key
        /// </param>
        /// <param name="fullClassName">
        /// Full class name of the type
        /// </param>
        /// <returns>
        /// The generic entity or null
        /// </returns>
        public GenericEntity Find(string id, string applicationKey, string fullClassName)
        {
            if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(applicationKey) || string.IsNullOrEmpty(fullClassName))
            {
                return null;
            }
            GenericEntityXmlFileDataStorage genericEntityXmlFileDataStorage = getGenericEntityXmlFileDataStorage(applicationKey, fullClassName);
            return genericEntityXmlFileDataStorage.Find(id, applicationKey, fullClassName);
        }

        /// <summary>
        /// Selects all generic entities with the given application key and type
        /// </summary>
        /// <param name="applicationKey">
        /// Application key
        /// </param>
        /// <param name="fullClassName">
        /// Full class name of the type
        /// </param>
        /// <returns>
        /// A list of generic entities
        /// </returns>
        public IList<GenericEntity> Select(string applicationKey, string fullClassName)
        {
            IList<GenericEntity> result = new List<GenericEntity>();
            if (string.IsNullOrEmpty(applicationKey) || string.IsNullOrEmpty(fullClassName))
            {
                return result;
            }
            GenericEntityXmlFileDataStorage genericEntityXmlFileDataStorage = getGenericEntityXmlFileDataStorage(applicationKey, fullClassName);
            return genericEntityXmlFileDataStorage.Select(applicationKey, fullClassName);
        }

        /// <summary>
        /// Deletes the given generic entity
        /// </summary>
        /// <param name="genericEntity">
        /// Generic entity to delete
        /// </param>
        public void Delete(GenericEntity genericEntity)
        {
            if (genericEntity == null)
            {
                return;
            }
            GenericEntityXmlFileDataStorage genericEntityXmlFileDataStorage = getGenericEntityXmlFileDataStorage(genericEntity.ApplicationKey, genericEntity.FullClassName);
            genericEntityXmlFileDataStorage.Delete(genericEntity);
        }

        /// <summary>
        /// Deletes the given list of generic entities
        /// </summary>
        /// <param name="genericEntities">
        /// List of generic entities to delete
        /// </param>
        public void Delete(IList<GenericEntity> genericEntities)
        {
            if (genericEntities == null || genericEntities.Count == 0)
            {
                return;
            }
            foreach (GenericEntity genericEntity in genericEntities)
            {
                GenericEntityXmlFileDataStorage genericEntityXmlFileDataStorage = getGenericEntityXmlFileDataStorage(genericEntity.ApplicationKey, genericEntity.FullClassName);
                genericEntityXmlFileDataStorage.Delete(genericEntity);
            }
        }

        /// <summary>
        /// Deletes all generic entities with the given application key and type
        /// </summary>
        /// <param name="applicationKey">
        /// Application key
        /// </param>
        /// <param name="fullClassName">
        /// Full class name of the type
        /// </param>
        public void Delete(string applicationKey, string fullClassName)
        {
            if (string.IsNullOrEmpty(applicationKey) || string.IsNullOrEmpty(fullClassName))
            {
                return;
            }
            GenericEntityXmlFileDataStorage genericEntityXmlFileDataStorage = getGenericEntityXmlFileDataStorage(applicationKey, fullClassName);
            genericEntityXmlFileDataStorage.Delete(applicationKey, fullClassName);
        }

        /// <summary>
        /// Deletes all generic entities with the given application key
        /// </summary>
        /// <param name="applicationKey">
        /// Application key
        /// </param>
        public void Delete(string applicationKey)
        {
            if (string.IsNullOrEmpty(applicationKey))
            {
                return;
            }
            foreach (string fullClassName in FindFullClassNames(applicationKey))
            {
                GenericEntityXmlFileDataStorage genericEntityXmlFileDataStorage = getGenericEntityXmlFileDataStorage(applicationKey, fullClassName);
                genericEntityXmlFileDataStorage.Delete(applicationKey, fullClassName);
            }
        }

        /// <summary>
        /// Finds all full class names with the given application key
        /// </summary>
        /// <param name="applicationKey">
        /// Application key
        /// </param>
        /// <returns>
        /// List of full class names or empty list
        /// </returns>
        public IList<string> FindFullClassNames(string applicationKey)
        {
            IList<string> result = new List<string>();
            if (string.IsNullOrEmpty(applicationKey))
            {
                return result;
            }
            if (Directory.Exists(getDirectoryName(applicationKey)))
            {
                foreach (string fullClassNameFileName in Directory.GetFiles(getDirectoryName(applicationKey)))
                {
                    if (fullClassNameFileName.ToLowerInvariant().EndsWith(".xml"))
                    {
                        try
                        {
                            GenericEntityXmlFileDataStorage genericEntityXmlFileDataStorage = new GenericEntityXmlFileDataStorage(fullClassNameFileName);
                            foreach (string fullClassName in genericEntityXmlFileDataStorage.FindFullClassNames(applicationKey))
                            {
                                if (!result.Contains(fullClassName))
                                {
                                    result.Add(fullClassName);
                                }
                            }
                        }
                        catch { }
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Finds all application keys
        /// </summary>
        /// <returns>
        /// List of application keys or empty list
        /// </returns>
        public IList<string> FindApplicationKeys()
        {
            IList<string> result = new List<string>();
            foreach (string applicationDirectoryName in Directory.GetDirectories(PathName))
            {
                foreach (string fullClassNameFileName in Directory.GetFiles(applicationDirectoryName))
                {
                    if (fullClassNameFileName.ToLowerInvariant().EndsWith(".xml"))
                    {
                        try
                        {
                            GenericEntityXmlFileDataStorage genericEntityXmlFileDataStorage = new GenericEntityXmlFileDataStorage(fullClassNameFileName);
                            foreach (string applicationKey in genericEntityXmlFileDataStorage.FindApplicationKeys())
                            {
                                if (!result.Contains(applicationKey))
                                {
                                    result.Add(applicationKey);
                                }
                            }
                        }
                        catch { }
                    }
                }
            }
            return result;
        }
        #endregion IGenericEntityDataStorage implementation


        #region helpers
        /// <summary>
        /// Get the generic entity xml file data storage for the given
        /// application key and full class name
        /// </summary>
        /// <param name="applicationKey">
        /// Application key
        /// </param>
        /// <param name="fullClassName">
        /// Full class name of the type
        /// </param>
        private GenericEntityXmlFileDataStorage getGenericEntityXmlFileDataStorage(string applicationKey, string fullClassName)
        {
            string directoryName = getDirectoryName(applicationKey);
            if (!Directory.Exists(directoryName))
            {
                Directory.CreateDirectory(directoryName);
            }
            return new GenericEntityXmlFileDataStorage(directoryName + "\\" + fullClassName + ".xml");
        }

        /// <summary>
        /// Gets the directory name for the given application key
        /// </summary>
        /// <param name="applicationKey">
        /// Application key
        /// </param>
        /// <returns>
        /// Directory name
        /// </returns>
        private string getDirectoryName(string applicationKey)
        {
            if (applicationKey != null)
            {
                if (applicationKey.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
                {
                    if (Directory.Exists(PathName))
                    {
                        foreach (string directoryName in Directory.GetDirectories(PathName))
                        {
                            foreach (string fileName in Directory.GetFiles(PathName + "\\" + directoryName))
                            {
                                if (fileName.ToLowerInvariant().EndsWith(".xml"))
                                {
                                    if (new GenericEntityXmlFileDataStorage(fileName).FindApplicationKeys().Contains(applicationKey))
                                    {
                                        return PathName + "\\" + directoryName;
                                    }
                                }
                                break;
                            }
                        }
                    }
                    return PathName + "\\" + Guid.NewGuid().ToString();
                }
            }
            return PathName + "\\" + applicationKey;
        }
        #endregion helpers

    }

}
